package write

import (
	// "crypto/tls"
	"context"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"strings"
	"sync"
	"time"

	"mongo-stress/point"

	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

const (
	DefaultDatabase        = "stress"
	DefaultRetentionPolicy = "autogen"
	DefaultCollection      = "metrics"
)

type Client interface {
	Create(string) error
	Send(docs []interface{}) (pointsWritten int, latNs int64, statusCode int, body string, err error)

	Close() error
}

// client 结构体实现 Client 接口，封装 MongoDB 驱动客户端sssssssss
type mongoClient struct {
	client     *mongo.Client     // 官方驱动客户端
	database   *mongo.Database   // 数据库实例
	collection *mongo.Collection // 集合实例
}

func NewClient(uri, dbName, collectionName string, timeOut int) *mongoClient {
	// 设置默认值
	if dbName == "" {
		dbName = DefaultDatabase
	}
	if collectionName == "" {
		collectionName = DefaultCollection
	}
	if timeOut <= 0 {
		timeOut = 10 // 默认10秒超时
	}

	// 创建上下文
	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeOut)*time.Second)
	defer cancel()

	// 构建连接选项
	clientOpts := options.Client().ApplyURI(uri)

	// 连接到 MongoDB
	c, err := mongo.Connect(ctx, clientOpts)
	if err != nil {
		return nil
	}

	// 获取数据库和集合引用
	database := c.Database(dbName)
	collection := database.Collection(collectionName)

	return &mongoClient{
		client:     c,
		database:   database,
		collection: collection,
	}
}

func DBExists(client *mongo.Client, dbName string) bool {
	//log.Println("tsbs_load_mongo/creator/DBExists")
	dbs, err := client.ListDatabaseNames(context.TODO(), bson.D{})
	if err != nil {
		log.Fatal(err)
	}
	for _, name := range dbs {
		if name == dbName {
			return true
		}
	}
	return false
}

func RemoveOldDB(client *mongo.Client, dbName string) error {
	//log.Println("tsbs_load_mongo/creator/RemoveOldDB")
	collection_names, err := client.Database(dbName).ListCollectionNames(context.TODO(), bson.D{})
	log.Printf("collection_names : %s", collection_names)
	if err != nil {
		return err
	}
	for _, coll := range collection_names {
		log.Printf("collection found :  %s", coll)
		log.Println("deleting the previous collection")
		err := client.Database(dbName).Collection(coll).Drop(context.TODO())
		if err != nil {
			log.Printf("Could not delete collection : %s", err.Error())
		}
	}
	return nil
}

// Create 方法用于创建集合，支持时间序列集合和普通集合
// command 参数用于传递创建集合的自定义配置（JSON格式）
func (c *mongoClient) Create(command string) error {
	// 解析自定义命令（如果提供）
	var cmdOpts map[string]interface{}
	if command != "" {
		if err := json.Unmarshal([]byte(command), &cmdOpts); err != nil {
			return fmt.Errorf("解析命令失败: %w", err)
		}
	}
	// 检查数据库是否存在
	if DBExists(c.client, c.database.Name()) {
		// 删除数据库
		err := RemoveOldDB(c.client, c.database.Name())
		if err != nil {
			return fmt.Errorf("remove old db err: %v", err)
		}
	}
	// Define time series options
	tsOptions := options.TimeSeries().
		SetTimeField("timestamp").
		SetMetaField("tags").
		SetGranularity("seconds") // Optional: seconds, minutes, hours

	// Define collection options
	collectionOptions := options.CreateCollection().SetTimeSeriesOptions(tsOptions)

	// Create the time series collection
	err := c.client.Database(c.database.Name()).CreateCollection(context.TODO(), c.collection.Name(), collectionOptions)
	if err != nil {
		if strings.Contains(err.Error(), "already exists") {
			log.Printf("collection %s already exists", c.collection.Name())
			return nil
		}
		log.Printf("create collection err: %v", err)
		return fmt.Errorf("create collection err: %v", err)
	}

	return nil
}

// Send 处理JSON格式的批量数据并插入到MongoDB
// 参数b: 包含多个文档的JSON数组字节（如[{"ticker":"AAPL",...},...]）
// 返回值:
//
//	 pointsWritten: 成功写入的文档数量
//		latNs: 操作延迟时间（纳秒）
//		statusCode: 自定义状态码（200=成功，400=数据格式错误，500=数据库错误）
//		body: 响应信息描述
//		err: 错误对象（非nil表示操作失败）
func (c *mongoClient) Send(docs []interface{}) (pointsWritten int, latNs int64, statusCode int, body string, err error) {
	startTime := time.Now()

	if len(docs) == 0 {
		return 0, time.Since(startTime).Nanoseconds(), 400, "输入数据为空", fmt.Errorf("输入数据为空")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	result, err := c.collection.InsertMany(ctx, docs)
	latNs = time.Since(startTime).Nanoseconds()

	if err != nil {
		return 0, latNs, 500, fmt.Sprintf("批量插入失败: %v", err), err
	}

	return len(result.InsertedIDs), latNs, 200, fmt.Sprintf("成功插入 %d 条文档", len(result.InsertedIDs)), nil
}

// SendPoints 支持直接插入由 NewPoints 生成的 []*point.Point
func (c *mongoClient) SendPoints(points []*point.Point) (pointsWritten int, latNs int64, statusCode int, body string, err error) {
	startTime := time.Now()

	if len(points) == 0 {
		latNs = time.Since(startTime).Nanoseconds()
		return 0, latNs, 400, "输入数据为空", fmt.Errorf("输入数据为空")
	}

	docs := make([]interface{}, len(points))
	for i, p := range points {
		docs[i] = p
	}

	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()
	result, err := c.collection.InsertMany(ctx, docs)
	latNs = time.Since(startTime).Nanoseconds()

	if err != nil {
		return 0, latNs, 500, fmt.Sprintf("批量插入失败: %v", err), err
	}

	return len(result.InsertedIDs), latNs, 200, fmt.Sprintf("成功插入 %d 条文档，ID列表: %v", len(result.InsertedIDs), result.InsertedIDs), nil
}

// Close 安全关闭MongoDB客户端连接，释放所有相关资源
// 返回关闭过程中可能出现的错误
func (c *mongoClient) Close() error {
	// 1. 检查客户端是否已初始化（避免空指针异常）
	if c.client == nil {
		return nil // 客户端未初始化，无需关闭
	}

	// 2. 创建带超时的上下文，确保关闭操作不会无限阻塞
	// 通常设置较短超时（如5秒），避免程序退出时等待过久
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel() // 确保上下文资源释放

	// 3. 调用官方驱动的Disconnect方法关闭连接
	// 该方法会关闭所有网络连接并释放连接池资源
	if err := c.client.Disconnect(ctx); err != nil {
		return fmt.Errorf("关闭MongoDB连接失败: %w", err)
	}

	// 4. 清空客户端引用（可选，帮助GC回收）
	c.client = nil
	c.database = nil
	c.collection = nil

	return nil
}

type fileClient struct {
	database string

	mu    sync.Mutex
	f     *os.File
	batch uint
}

// NewFileClient 创建 fileClient
func NewFileClient(path string) (*fileClient, error) {
	c := &fileClient{}

	var err error
	c.f, err = os.Create(path)
	if err != nil {
		return nil, err
	}

	// 可选：写文件头信息
	if _, err := c.f.WriteString("# fileClient output\n"); err != nil {
		return nil, err
	}

	return c, nil
}

// Create 写入创建数据库命令
func (c *fileClient) Create(command string) error {
	if command == "" {
		command = "CREATE DATABASE " + c.database
	}

	c.mu.Lock()
	defer c.mu.Unlock()
	_, err := fmt.Fprintf(c.f, "# create: %s\n\n", command)
	return err
}

// Send 接收 []interface{} 并写入文件
func (c *fileClient) Send(docs []interface{}) (latNs int64, statusCode int, body string, err error) {
	c.mu.Lock()
	defer c.mu.Unlock()

	start := time.Now()
	defer func() {
		latNs = time.Since(start).Nanoseconds()
	}()

	// 增加批次
	c.batch++
	if _, err = fmt.Fprintf(c.f, "# Batch %d:\n", c.batch); err != nil {
		return
	}

	// 转 JSON 写入
	for _, doc := range docs {
		line, e := json.Marshal(doc)
		if e != nil {
			err = e
			return
		}
		if _, err = c.f.Write(line); err != nil {
			return
		}
		if _, err = c.f.Write([]byte{'\n'}); err != nil {
			return
		}
	}

	statusCode = 204
	return
}

// Close 关闭文件
func (c *fileClient) Close() error {
	return c.f.Close()
}
